/*
 * AVFramePtrQueue.h
 *
 *  Created on: 2018年8月13日
 *      Author: zhengboyuan
 */

#ifndef AVFRAMEPTRQUEUE_H_
#define AVFRAMEPTRQUEUE_H_

#include "BasicType.h"
#include "AVFramePtr.h"
#include "MediaHelper.h"


namespace av
{

class AVFramePtrQueue
{
public:
	AVFramePtrQueue():
		m_videoFrameCount()
	{
	}

	virtual ~AVFramePtrQueue()
	{
		clear();
	}

	size_t push(AVFramePtr& frame)
	{
		comn::AutoCritSec lock(m_cs);
		m_frames.push_back(frame);
		if (MediaHelper::isVideo(frame.get()))
		{
			m_videoFrameCount++;
		}
		return m_frames.size();
	}

	AVFramePtr pop()
	{
		comn::AutoCritSec lock(m_cs);
		AVFramePtr frame;
		if (!m_frames.empty())
		{
			frame = m_frames.front();
			m_frames.pop_front();

			if (MediaHelper::isVideo(frame.get()))
			{
				m_videoFrameCount --;
			}
		}
		return frame;
	}

	/// 头部pts <= minPts 或者队列时长超过最大值时, 取出头部元素
	AVFramePtr pop(int64_t minPts, int maxDuration)
	{
		comn::AutoCritSec lock(m_cs);
		AVFramePtr frame;
		if (!m_frames.empty())
		{
			int64_t duration = getQueueDuration();
			if (((maxDuration > 0) && (duration > maxDuration)) || (minPts > 0 && m_frames.front()->pts <= minPts))
			{
				frame = m_frames.front();
				m_frames.pop_front();

				if (MediaHelper::isVideo(frame.get()))
				{
					m_videoFrameCount--;
				}
			}
		}
		return frame;
	}

	size_t size()
	{
		comn::AutoCritSec lock(m_cs);
		return m_frames.size();
	}

	void clear()
	{
		comn::AutoCritSec lock(m_cs);
		while (!m_frames.empty())
		{
			AVFramePtr frame = m_frames.front();
			m_frames.pop_front();
		}

		m_videoFrameCount = 0;
	}

	size_t popUntil(int64_t pts)
	{
		size_t count = 0;
		comn::AutoCritSec lock(m_cs);
		while (!m_frames.empty())
		{
			AVFramePtr frame = m_frames.front();
			int64_t framePts = (frame->pts == AV_NOPTS_VALUE) ? frame->pkt_pts : frame->pts;
			if (framePts <= pts)
			{
				m_frames.pop_front();
				count++;

				if (MediaHelper::isVideo(frame.get()))
				{
					m_videoFrameCount--;
				}
			}
			else
			{
				break;
			}
		}
		return count;
	}

	AVFramePtr pop(size_t& queSize)
	{
		comn::AutoCritSec lock(m_cs);
		AVFramePtr frame = NULL;
		if (!m_frames.empty())
		{
			frame = m_frames.front();
			m_frames.pop_front();

			queSize = m_frames.size();

			if (MediaHelper::isVideo(frame.get()))
			{
				m_videoFrameCount--;
			}
		}
		return frame;
	}

	size_t popUntilSize(size_t size)
	{
		size_t count = 0;
		comn::AutoCritSec lock(m_cs);
		while (m_frames.size() > size)
		{
			AVFramePtr frame = m_frames.front();
			m_frames.pop_front();
			count++;

			if (MediaHelper::isVideo(frame.get()))
			{
				m_videoFrameCount--;
			}
		}
		return count;
	}

	int64_t getDuration()
	{
		comn::AutoCritSec lock(m_cs);
		int64_t duration = getQueueDuration();
		return duration;
	}

	int getVideoFrameCount()
	{
		return m_videoFrameCount;
	}

protected:

	int64_t getQueueDuration()
	{
		int64_t duration = 0;
		if (m_frames.size() > 1)
		{
			duration = m_frames.back()->pts - m_frames.front()->pts;
		}
		return duration;
	}

protected:
	typedef std::deque< AVFramePtr >  FrameDeque;

	FrameDeque  m_frames;
	comn::CriticalSection   m_cs;
	int	m_videoFrameCount;

};

} /* namespace av */

#endif /* AVFRAMEPTRQUEUE_H_ */
